package org.ogv.usada.models;

import org.ogv.OGV;
import org.ogv.spritesheet.SpriteSheet;
import org.ogv.usada.views.CustomerBubble;

import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.graphics.g2d.BitmapFontCache;
import com.badlogic.gdx.graphics.g2d.Sprite;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.utils.Pool;

public class Customer {
	private CustomerDef			_def;
	private IllnessDef			_illnessDef;
	private SpriteSheet			_spriteSheet;
	private Sprite				_star;
	private int 				_state;
	private float				_curWaitTime;
	private int					_patienceLevel;
	private boolean				_hasDone;
	private CustomerBubble 		_bubble;
	private Recipe				_recipe;
	private BitmapFontCache 	_bmFont;
	private float				_fontLifetime;
	private CustomerListener 	_listener;
	
	public Customer init(float posX, float posY, byte custType, byte illnessType, CustomerListener listener) {
		//initialize
		_def 			= Content.$().customers[custType];
		_illnessDef 	= Content.$().illnesses[illnessType];
		_spriteSheet.init(_def.spriteSheet);
		_spriteSheet.setPosition(posX - _spriteSheet.getWidth() / 2, posY);
		_curWaitTime	= 0;
		_patienceLevel	=-1;
		_hasDone 		= false;
		_bubble.init(_illnessDef, _recipe.init(_illnessDef.recipeId));
		_bubble.setPosition(_spriteSheet.getX() + _spriteSheet.getWidth()/2 + 50, _spriteSheet.getY() + _spriteSheet.getHeight() - 100);
		_fontLifetime 	= 0;
		_listener		= listener;
		
		//enter state just arrive
		changeState(-1, 0);
		
		return this;
	}
	
	public boolean isTouched(float x, float y) {
		return _def.boundingRect.contains(x - _spriteSheet.getX(), y - _spriteSheet.getY());
	}
	
	public void showWrongLeafMinus() {
		spawnFont(Color.RED, "" + GP.$().GAME_LEAF_WRONG_SCORE, 2);
		_listener.onCustomerPay(this, GP.$().GAME_LEAF_WRONG_SCORE);
	}
	
	public void update(float elapsedTime) {
		//update based on current state
		switch (_state) {
		case 0:
			doJustArrive(elapsedTime);
			break;
		case 1:
			doWaitForMedicine(elapsedTime);
			break;
		case 2:
			doAngryOut(elapsedTime);
			break;
		case 3:
			doPayAndOut(elapsedTime);
			break;
		default:
			break;
		}
		
		if(_fontLifetime > 0) {
			_fontLifetime -= elapsedTime;
			_bmFont.translate(0, 100 * elapsedTime);
		}
	}

	private void changeState(int before, int after) {
		switch (after) {
		//Just arrive
		case 0:
			_spriteSheet.setAnim(CustomerDef.ANIM_WAVE);
			break;
			
		//wait for med
		case 1:
			_spriteSheet.translate(0, -50);
			break;
			
		//angry out
		case 2:
			_spriteSheet.translate(0, 50);
			_spriteSheet.setAnim(CustomerDef.ANIM_ANGRY);
			spawnFont(Color.RED, "" + GP.$().GAME_CUST_ANGRY_SCORE, 2);
			_listener.onCustomerPay(this, GP.$().GAME_CUST_ANGRY_SCORE);
			break;
			
		//pay and out
		case 3:
			_spriteSheet.translate(0, 50);
			_spriteSheet.setAnim(CustomerDef.ANIM_WAVE);
			spawnFont(Color.GREEN, "" + getScore(), 2);
			_listener.onCustomerPay(this, getScore());
			break;
			
		default:
			break;
		}
		
		_state = after;
	}

	private void doPayAndOut(float elapsedTime) {
		_spriteSheet.update(elapsedTime);
		_hasDone = _spriteSheet.isAnimEnded();
	}
	
	private void doAngryOut(float delta) {
		_spriteSheet.update(delta);
		_hasDone = _spriteSheet.isAnimEnded();
	}
	
	private void doWaitForMedicine(float delta) {
		_spriteSheet.update(delta);
		_curWaitTime += delta;
		
		int newPatienceLevel = (int) Math.ceil((1 - (_curWaitTime/_def.waitTime)) * 3);
		if(_patienceLevel != newPatienceLevel) {
			//change anim based on patience level
			_patienceLevel = newPatienceLevel;
			switch (_patienceLevel) {
			case 3:
				_spriteSheet.setAnim(CustomerDef.ANIM_SIT_3STAR);
				break;
			case 2:
				_spriteSheet.setAnim(CustomerDef.ANIM_SIT_2STAR);
				break;
			case 1:
				_spriteSheet.setAnim(CustomerDef.ANIM_SIT_1STAR);
				break;
				
			//run out of patience.. I'm fucking out!
			case 0:
				changeState(_state, 2);
				break;
			default:
				break;
			}
		}
	}

	private void doJustArrive(float delta) {
		_spriteSheet.update(delta);
		if(_spriteSheet.isAnimEnded()) changeState(_state, 1);
	}

	public void draw(SpriteBatch sb) {
		_spriteSheet.draw(sb);

		//debug draw
		if(GP.$().DEBUG_DRAW) _def.boundingRect.draw(sb, _spriteSheet.getX(), _spriteSheet.getY());
		
		//draw stars
		int starsToDraw = _patienceLevel;
		int offset = (int)(_spriteSheet.getWidth() - _star.getWidth() * starsToDraw) >> 1;
		for(int i = 0; i < starsToDraw; ++i) {
			_star.setPosition(
				_spriteSheet.getX() + i * _star.getWidth() + offset, 
				_spriteSheet.getY() + 80
			);
			_star.draw(sb);
		}
		
		//draw bubble
		if(_state == 1) {
			_bubble.draw(sb);
		} 
		
		if(_fontLifetime > 0) {
			_bmFont.draw(sb);
		}
	}
	
	private void spawnFont(Color color, String text, float lifetime) {
		_bmFont.setColor(color);
		_bmFont.setText(text,0,0);
		_bmFont.setPosition(
			_spriteSheet.getX() + (_spriteSheet.getWidth() - _bmFont.getBounds().width) / 2, 
			_spriteSheet.getY() + _spriteSheet.getHeight()
		);
		_fontLifetime = lifetime;
	}

	public boolean hasDone() {
		return _hasDone;
	}

	public boolean isWaitingForMed() {
		return _state == 1;
	}

	public void payAndGoHome() {
		changeState(_state, 3);
	}
	
	public void increaseHappiness() {
		_curWaitTime += 10;
	}

	public void showRecipe(boolean val) {
		_bubble.recipeVisible(val);
	}

	public Recipe getRecipe() {
		return _recipe;
	}
	
	public int getScore() {
		return _def.baseScore * _patienceLevel / 3;
	}
	
	/////Object Management ////////////////////////////////////////////////////////////////
	private static Pool<Customer> _POOL = new Pool<Customer>() {
		@Override
		protected Customer newObject() {
			return new Customer();
		}
	};	
	public static void clearPool() 	{ _POOL.clear(); }
	public static Customer obtain()	{ return _POOL.obtain(); }
	public void free()				{ _POOL.free(this); } 
	private Customer() {
		_spriteSheet 	= new SpriteSheet();
		_star			= new Sprite(OGV.asset.getTextureRegion(GP.$().RES_TEXR_STAR));
		_bubble			= new CustomerBubble();
		_recipe			= new Recipe();
		_bmFont			= new BitmapFontCache(OGV.asset.get(GP.$().RES_FONT_PRINTCLEARLY, BitmapFont.class));
		_bmFont.setColor(0, 1, 0, 1);
	}
	
	
	
	public static interface CustomerListener {
		public void onCustomerPay(Customer c, int value);
	}
}
